package com.net.xpay.web.controller.merchant.safePay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.response.AlipayFundAccountbookCloseResponse;
import com.alipay.api.response.AlipayFundAccountbookQueryResponse;
import com.alipay.api.response.AlipayUserAgreementUnsignResponse;
import com.beust.jcommander.internal.Lists;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.model.*;
import com.net.xpay.web.vo.SafePayBatchProcessVo;
import com.net.xpay.web.vo.SafePayBatchVo;
import com.net.common.exception.BusinessException;
import com.net.common.model.ResultData;
import com.net.common.model.ResultPagedList;
import com.net.common.util.AlipayAccountCheckUtil;
import com.net.common.util.NetUtil;
import com.net.common.util.SerializationUtil;
import com.net.xpay.alipay.safePay.PoseidonSafePayAlipayClient;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.helper.RedisHelper;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.RedisKey;
import com.net.xpay.core.domain.AccountLog;
import com.net.xpay.core.domain.safePay.BookAccountChargeOrder;
import com.net.xpay.core.domain.safePay.SafePayBatch;
import com.net.xpay.core.domain.safePay.SafePayBookAccount;
import com.net.xpay.core.manager.AccountLogManager;
import com.net.xpay.core.manager.safePay.SafePayBatchManager;
import com.net.xpay.core.manager.safePay.SafePayBookAccountManager;
import com.net.xpay.core.manager.safePay.SafePayOrderManager;
import com.net.xpay.core.service.safePay.SafePayService;
import com.net.xpay.core.utils.BeanCopierUtil;
import com.net.xpay.core.utils.JxlsUtils;
import com.net.xpay.web.secutiry.RequestUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@Slf4j
@RequestMapping("/merchant/safe-pay")
@Api(tags = "商户/安全付", description = "商户 安全付 net")
public class SafePayController {



    @Autowired
    private RedisHelper redisHelper;

    @Autowired
    private PoseidonSafePayAlipayClient xpaySafePayAlipayClient;

    @Autowired
    private SafePayBookAccountManager safePayBookAccountManager;

    @Autowired
    private SafePayService safePayService;

    @Autowired
    private SafePayBatchManager safePayBatchManager;

    @Autowired
    private SafePayOrderManager safePayOrderManager;

    @Autowired
    private UserProfileManager userProfileManager;

    @Autowired
    private AccountLogManager accountLogManager;



    /**
     * 签约，获取签约链接
     */
    @ApiOperation(value = "签约，获取签约链接")
    @GetMapping("/sign")
    @ResponseBody
    public ResultData<String> getSafePaySignPage(@ApiIgnore @RequestUser User user, String alipayNickName) {
        log.info("getSafePaySignPage|alipayNickName={}", alipayNickName);
        if (Strings.isNullOrEmpty(alipayNickName)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入支付宝账号");
        }
        alipayNickName = alipayNickName.trim();

        if (!AlipayAccountCheckUtil.isEffectiveAccount(alipayNickName)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账号格式错误");
        }

        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (!Boolean.TRUE.equals(userProfile.getOpenSafePay())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "尚未开通安全付功能");
        }
        return new ResultData<>(doGetSignPage(alipayNickName, user));
    }


    /**
     * 新签约方式 签约，获取签约链接
     */
    @ApiOperation(value = "签约，获取签约链接")
    @PostMapping("/sign2")
    @ResponseBody
    public ResultData<String> getSafePaySignPage2(@ApiIgnore @RequestUser User user, @RequestBody SafePayPageSignModel signModel) {
        log.info("getSafePaySignPage2|signModel={}", SerializationUtil.obj2String(signModel));

        return new ResultData<>(safePayService.getSignPage(user, signModel));
    }


    /**
     * 解约支付宝账号
     */
    @ApiOperation("解约支付宝账号")
    @PostMapping("/unsign")
    public ResultData<Boolean> unsignAlipayUser(@ApiIgnore @RequestUser User merchant, Long id) {

        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findById(merchant.getId(), id);
        if (safePayBookAccount == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账户不存在");
        }
        if (safePayBookAccount.getSignStatus() != SafePayBookAccountSignStatus.NORMAL) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账户已解约");
        }

        //余额不为0暂时不能解除
        try {
            AlipayFundAccountbookQueryResponse resp;
            resp = xpaySafePayAlipayClient.queryAlipayFundAccountbook(safePayBookAccount.getBookId(), safePayBookAccount.getAgreementNo());
            if (resp.isSuccess()) {
                if (new BigDecimal(resp.getAvailableAmount()).compareTo(new BigDecimal("0")) > 0) {
                    throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "账户本金余额不为0，暂无法解约");
                }
            }
        } catch (AlipayApiException ignored) {}

        //注销
        try {
            AlipayFundAccountbookCloseResponse alipayFundAccountbookCloseResponse = xpaySafePayAlipayClient.closeAlipayFundAccountbook(safePayBookAccount.getBookId(), safePayBookAccount.getAgreementNo());
            if (!alipayFundAccountbookCloseResponse.isSuccess()) {
                log.info("closeAlipayFundAccountbook failed|resp={}", SerializationUtil.obj2String(alipayFundAccountbookCloseResponse));
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "注销失败：" + alipayFundAccountbookCloseResponse.getMsg() + "|" + alipayFundAccountbookCloseResponse.getSubMsg());
            }
            log.info("closeAlipayFundAccountbook success|resp={}", SerializationUtil.obj2String(alipayFundAccountbookCloseResponse));
        } catch (AlipayApiException e) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统繁忙");
        }


        try {
            AlipayUserAgreementUnsignResponse resp = xpaySafePayAlipayClient.unSignAlipayUserAgreement(safePayBookAccount.getExternalAgreementNo());
            if (!resp.isSuccess()) {
                log.info("unsignAlipayUser failed|userId={}|id={}|errorCode={}|errorMsg={}", merchant.getId(), id, resp.getCode() + "|" + resp.getSubCode(), resp.getMsg() + "|" + resp.getSubMsg());
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "解约失败：" + resp.getMsg() + ";" + resp.getSubMsg());
            }
            log.info("unSignAlipayUserAgreement success|resp={}", SerializationUtil.obj2String(resp));
        } catch (AlipayApiException e) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统繁忙");
        }

        safePayBookAccount.setSignStatus(SafePayBookAccountSignStatus.STOP);
        safePayBookAccountManager.updateByPrimaryKeyWithVersion(safePayBookAccount);

        return new ResultData<>(true);
    }


    /**
     * 开始打款
     */
    @ApiOperation(value = "开始打款")
    @PostMapping("/batch-trans")
    @ResponseBody
    public ResultData<SafePayBatchVo> transSafePayBatch(@ApiIgnore @RequestUser User user, @RequestBody SafePayBatchTransModel transModel, HttpServletRequest request) {
        log.info("transSafePayBatch|params={}", SerializationUtil.obj2String(transModel));
        if (Strings.isNullOrEmpty(transModel.getPayPassword())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入支付密码!");
        }

        //支付密码已连续输入5次，请5分钟后再试！
        long errNum  = 0L;
        Object errCount = redisHelper.get(RedisKey.MCH_PAY_PASSWORD_ERR_CNT + ":" + user.getId());
        if (errCount != null) {
            errNum = Long.parseLong(errCount.toString());
            if (errNum >= 5L) {
                log.warn("transSafePayBatch password not right max|userId={}", user.getId());
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "密码连续输错5次已被限制打款，请5分钟后再试");
            }
        }

        if (Strings.isNullOrEmpty(user.getPayPassword())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请先设置支付密码");
        }

        if (!BCrypt.checkpw(transModel.getPayPassword(), user.getPayPassword())) {
            errNum++;
            redisHelper.set(RedisKey.MCH_PAY_PASSWORD_ERR_CNT + ":" + user.getId(), errNum, 5 * 60L);
            log.warn("verifyPayPassword psw wrong userId={}|password={}|ip={}", user.getId(), transModel.getPayPassword(), NetUtil.getRequestRealIp(request));
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付密码不正确!");
        }
        //密码输入正确，删除计数
        redisHelper.remove(RedisKey.MCH_PAY_PASSWORD_ERR_CNT + ":" + user.getId());

        SafePayBatch safePayBatch = safePayService.transSafePayBatch(user, transModel);
        SafePayBatchVo vo = SafePayBatchVo.getInstance();
        BeanCopierUtil.copy(safePayBatch, vo);
        return new ResultData<>(vo);
    }

    /**
     * 安全付手动输入打款名单
     */
    @ApiOperation(value = "安全付手动输入打款名单")
    @PostMapping("/manual-import-safe-pay-trans")
    @ResponseBody
    public ResultData<SafePayBatchVo> manualImportSafePayTrans(@ApiIgnore @RequestUser User user, @RequestBody ManualSafePayTransModel transModel) {
        log.info("manualImportSafePayTrans|params={}", SerializationUtil.obj2String(transModel));

        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (Boolean.TRUE.equals(userProfile.getLimitSafePayWebPageTrans())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "权限未开通");
        }

        if (Strings.isNullOrEmpty(transModel.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入支付宝账号");
        }

        if (!AlipayAccountCheckUtil.isEffectiveAccount(transModel.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账号格式错误");
        }

        SafePayBatch safePayBatch = safePayService.manualImportSafePayTrans(user, transModel);
        SafePayBatchVo vo = SafePayBatchVo.getInstance();
        BeanCopierUtil.copy(safePayBatch, vo);
        return new ResultData<>(vo);
    }


    /**
     * 获取转账进度
     */
    @ApiOperation(value = "获取转账进度")
    @GetMapping("/progress")
    @ResponseBody
    public ResultData<SafePayBatchProcessVo> getTransProgress(@ApiIgnore @RequestUser User user, Long id) {
        Preconditions.checkArgument(id != null);
        SafePayBatch safePayBatch = safePayBatchManager.findById(user.getId(), id);
        if (safePayBatch == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次不存在");
        }
        SafePayBatchVo batchVo = SafePayBatchVo.getInstance();
        BeanCopierUtil.copy(safePayBatch, batchVo);

        SafePayBatchProcessVo vo = SafePayBatchProcessVo.getInstance();
        if (SafePayBatchStatus.WAIT_ENSURE == safePayBatch.getStatus()) {
            vo.setTotalNum(safePayBatch.getTotalCount());
            vo.setFinishNum(0L);
            return new ResultData<>(vo);
        }

        if (SafePayBatchStatus.isFinalStatus(safePayBatch.getStatus())) {
            vo.setTotalNum(safePayBatch.getTotalCount());
            vo.setFinishNum(vo.getTotalNum());
            vo.setBatch(batchVo);
            return new ResultData<>(vo);
        }
        SafePayOrderSearchModel searchModel = new SafePayOrderSearchModel();
        searchModel.setStatusList(Lists.newArrayList(SafePayOrderStatus.WAIT_TRANS, SafePayOrderStatus.DEALING));
        searchModel.setBatchId(id);
        long count = safePayOrderManager.searchCount(searchModel);

        vo.setTotalNum(safePayBatch.getTotalCount());
        vo.setFinishNum(vo.getTotalNum() - count);

        return new ResultData<>(vo);
    }

    /**
     * 搜索
     */
    @ApiOperation("搜索")
    @PostMapping("/search")
    public ResultPagedList<SafePayBatchVo> searchSafePayBatch(@ApiIgnore @RequestUser User merchant, @RequestBody SafePayBatchSearchModel searchModel) {
        Preconditions.checkArgument(searchModel != null);
        searchModel.setUserId(merchant.getId());

        List<SafePayBatch> payTransBatchList = safePayBatchManager.search(searchModel);
        List<SafePayBatchVo> vos = BeanCopierUtil.batchCopy(SafePayBatch.class, SafePayBatchVo.class, payTransBatchList);
        for (SafePayBatchVo vo : vos) {
            if (Lists.newArrayList(SafePayBatchStatus.DISUSE, SafePayBatchStatus.FAIL, SafePayBatchStatus.PART_SUCCESS).contains(vo.getStatus())
                    && vo.getServicePrice() != null && vo.getServicePrice() != 0L)  {
                AccountLog accountLog = accountLogManager.findByRelatedId(merchant.getId(), AccountBillType.SAFE_PAY_TRANS_SERVICE_PRICE_REFUND, vo.getId());
                if (accountLog == null) {
                    continue;
                }
                vo.setRefund(accountLog.getChange());
                vo.setRefundId(accountLog.getId());
            }
        }
        long count = safePayBatchManager.searchCount(searchModel);

        return new ResultPagedList<>(vos, count, searchModel);
    }

    /**
     * 打款批次导出
     */
    @ApiOperation("打款批次导出")
    @GetMapping("/export")
    public ResultData<Boolean> exportSafePayBatch(@ApiIgnore HttpServletRequest request, @ApiIgnore HttpServletResponse response, @ApiIgnore @RequestUser User merchant, String searchModelJson) {
        Preconditions.checkArgument(searchModelJson != null);
        SafePayBatchSearchModel searchModel = SerializationUtil.str2Object(searchModelJson, SafePayBatchSearchModel.class);
        searchModel.setUserId(merchant.getId());
        long count = safePayBatchManager.searchCount(searchModel);
        if (count > 10000L) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "导出数据超过10000条，无法导出");
        }

        List<SafePayBatch> records = safePayBatchManager.list(searchModel);
        if (records.size() > 10000L) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "导出数据超过10000条，无法导出");
        }

        Map<String, Object> model = new HashMap<>();
        model.put("records", records);

        String fileName = "安全付转账记录.xlsx";
        String template = "/excel/tpl/安全付转账记录.xls";

        InputStream is;
        try {
            //获取模板流
            is = getClass().getResourceAsStream(template);

            //火狐游览器中文文件名会有乱码,兼容火狐
            String agent = request.getHeader("USER-AGENT").toLowerCase();

            if (!Strings.isNullOrEmpty(agent) && (agent.contains("firefox") || agent.contains("safari"))) {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            } else {
                fileName = URLEncoder.encode(fileName, "UTF-8");
            }
            response.setContentType("application/force-download");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            JxlsUtils.exportExcel(is, response.getOutputStream(), model);
            response.flushBuffer();
        } catch (IOException e) {
            log.warn("export error", e);
        }

        return new ResultData<>();
    }

    /**
     * 获取单条批次
     */
    @ApiOperation("获取单条批次")
    @GetMapping("/get-batch")
    public ResultData<SafePayBatchVo> getSafePayBatch(@ApiIgnore @RequestUser User merchant, Long id) {
        Preconditions.checkArgument(id != null);

        SafePayBatch safePayBatch = safePayBatchManager.findById(merchant.getId(), id);
        SafePayBatchVo vo = SafePayBatchVo.getInstance();
        BeanCopierUtil.copy(safePayBatch, vo);

        return new ResultData<>(vo);
    }


    /**
     * 关闭批次
     */
    @ApiOperation(value = "关闭批次")
    @PostMapping("/close-batch")
    @ResponseBody
    public ResultData<Boolean> closeSafePayBatch(@ApiIgnore @RequestUser User merchant, Long id) {
        log.info("closeSafePayBatch|id={}", id);
        safePayService.closeSafePayBatchById(merchant, id);
        return new ResultData<>(true);
    }

    /**
     * 资金拨入账本
     */
    @ApiOperation(value = "资金拨入账本")
    @PostMapping("/charge")
    @ResponseBody
    public ResultData<String> chargeSafePayBook(@ApiIgnore @RequestUser User user, @RequestBody BookAccountChargeOrderModel chargeModel) {
        log.info("chargeSafePayBook|params={}", SerializationUtil.obj2String(chargeModel));
        if (Strings.isNullOrEmpty(chargeModel.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入支付宝账号");
        }
        chargeModel.setAlipayNickName(chargeModel.getAlipayNickName().trim());
        if (!AlipayAccountCheckUtil.isEffectiveAccount(chargeModel.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账号格式错误");
        }

        chargeModel.setImportBizNo("AUTO" + '-' + System.currentTimeMillis());

        BookAccountChargeOrder order = safePayService.createBookAccountChargeOrder(user, chargeModel);
        return new ResultData<>(order.getPayUrl());
    }


    @ApiOperation("获取确认转账验证码")
    @GetMapping("/check-code")
    @ApiImplicitParam(name = "batchId", value = "批次号")
    public ResultData<Boolean> getCheckCode(HttpServletRequest request, @ApiIgnore @RequestUser User user, Long batchId,
                                            Integer useSpareReceiveMobile) {
        safePayService.sendCheckCode(user, batchId, NetUtil.getRequestRealIp(request), useSpareReceiveMobile);
        return new ResultData<>(true);
    }


    private String doGetSignPage(String alipayNickName, User user) {
        Long userId = user.getId();
        //先从缓存中查，如果没有再请求新的
        Object authUrl = redisHelper.get(RedisKey.SAFE_PAY_SIGN_URL + ":" + user.getId() + ":" + alipayNickName);
        if (authUrl != null) {
            return authUrl.toString();
        }

        String externalAgreementNo = userId + "-" + System.currentTimeMillis();

        String authPageUrl = xpaySafePayAlipayClient.alipayUserAgreementPageSignCreate(externalAgreementNo, alipayNickName);

        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByAlipayNickName(userId, alipayNickName);
        if (safePayBookAccount == null) {
            safePayBookAccount = SafePayBookAccount.getInstance();
            safePayBookAccount.setUserId(userId);
            safePayBookAccount.setAlipayNickName(alipayNickName);
            safePayBookAccount.setExternalAgreementNo(externalAgreementNo);
            safePayBookAccount.setSignStatus(SafePayBookAccountSignStatus.TEMP);
            safePayBookAccount.setBookStatus(SafePayBookAccountBookStatus.NO);
            safePayBookAccount.setAuditStatus(SafePayBookAccountAuditStatus.INIT);
            safePayBookAccount.setSignMethod(SafePayBookAccountSignMethod.OLD);
            safePayBookAccountManager.save(safePayBookAccount);
        } else {
            log.warn("doGetSignPage safePayBookAccount before={}|alipayNickName={}", SerializationUtil.obj2String(safePayBookAccount), alipayNickName);
            if (safePayBookAccount.getSignStatus().equals(SafePayBookAccountSignStatus.NORMAL)) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "已签约，请勿重复签约");
            }
            safePayBookAccount.setExternalAgreementNo(externalAgreementNo);
            safePayBookAccount.setSignMethod(SafePayBookAccountSignMethod.OLD);
            safePayBookAccountManager.updateByPrimaryKeyWithVersion(safePayBookAccount);
        }
        redisHelper.set(RedisKey.SAFE_PAY_SIGN_URL + ":" + user.getId() + ":" + alipayNickName, authPageUrl, 60 * 30L);
        return authPageUrl;
    }
}
